﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Xml.Linq;
using System.Linq;
using AR_Designer.ARControl;
using SLARToolKit;



namespace ARDesigner
{
    public partial class MainPage : UserControl
    {

        public CaptureSourceMarkerDetector arDetector { get; set; }
        private Matrix3D projectionMatrix;
        private CaptureSource captureSource;
        private BindedARControlsList bindedControls = new BindedARControlsList();
        private List<UnbindedARControl> unbindedControls = new List<UnbindedARControl>();

        public MainPage()
        {
            InitializeComponent();


            Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);
            Application.Current.Host.Content.FullScreenChanged += new EventHandler(Content_Resized);

        }

        private IList<Marker> ApplicationMarkers()
        {
            var testPat1 = Marker.LoadFromResource("markers/test1.pat", 16, 16, 55.0, "t1");
            var testPat2 = Marker.LoadFromResource("markers/test2.pat", 16, 16, 55.0, "t2");
            var testPat3 = Marker.LoadFromResource("markers/test3.pat", 16, 16, 55.0, "t3");

            return new List<Marker> {
                
                testPat1,testPat2,testPat3 
            };
        }


        void Content_Resized(object sender, EventArgs e)
        {
            this.Width = Application.Current.Host.Content.ActualWidth;
            this.Height = Application.Current.Host.Content.ActualHeight;
            FullScreen.IsChecked = Application.Current.Host.Content.IsFullScreen;
        }

        private void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Ask user for permission and start the capturing
            if (CaptureDeviceConfiguration.AllowedDeviceAccess || CaptureDeviceConfiguration.RequestDeviceAccess())
            {
                HelloText.Visibility = System.Windows.Visibility.Collapsed;


                // Start capturing
                if (captureSource.State != CaptureState.Started)
                {
                    captureSource.Stop();

                    // Create video brush and fill the WebcamVideo rectangle with it
                    var vidBrush = new VideoBrush();
                    vidBrush.Stretch = Stretch.Uniform;
                    vidBrush.SetSource(captureSource);
                    WebcamVideo.Fill = vidBrush;


                    captureSource.Start();

                 }
            }
        }

        
        private void AttachMarkerDetectedEvent()
        {
            var dispatcher = Application.Current.RootVisual.Dispatcher;
            arDetector.MarkersDetected += (source, evnt) =>
            {
                // get detected markers
                var detectedMarkers = evnt.DetectionResults;
                projectionMatrix = arDetector.Projection;

                // invoke drawing because event comes from the background thread
                dispatcher.BeginInvoke(() =>
                {

                    if (detectedMarkers.HasResults)
                    {
                        WriteableBitmap markerHightlightBmp = new WriteableBitmap(evnt.BufferWidth, evnt.BufferHeight);
                        foreach (var marker in detectedMarkers)
                        {
                            Console.WriteLine(marker.Marker.Name);
                            if (marker.Confidence > confidence.Value / 100.0)
                            {
                                // draw control
                                DrawControl(marker);

                                if (highlighMarkers.IsChecked == true)
                                {
                                    HighlightMarkers(marker, markerHightlightBmp);
                                }

                            }

                        }

                        this.ViewportOverlay.Source = markerHightlightBmp;
                    }

                    // we want to turn off all the controls that were not displayed for a second
                    var controlsToHide = from c in bindedControls
                                         where c.LastSeen > new TimeSpan(0, 0, 1)
                                         select c;

                    foreach (var control in controlsToHide)
                    {
                        control.Control.Visibility = System.Windows.Visibility.Collapsed;
                    }
                                         
                    
                });
            };
        }

        private void HighlightMarkers(DetectionResult marker, WriteableBitmap markerHightlightBmp)
        {
            // draw red overlay
            markerHightlightBmp.DrawQuad((int)marker.Square.P1.X, (int)marker.Square.P1.Y,
                                    (int)marker.Square.P2.X, (int)marker.Square.P2.Y,
                                    (int)marker.Square.P3.X, (int)marker.Square.P3.Y,
                                    (int)marker.Square.P4.X, (int)marker.Square.P4.Y, Colors.Red);
        }


        private void DrawControl(DetectionResult marker)
        {

            var result = from control in bindedControls
                         where control.MarkerID == marker.Marker.Name
                         select control;
            BindedARControl elementToDisplay = null;

            if (result.Count() <= 0 && unbindedControls.Count > 0)
            {
                // bind new control with first time seen marker
                var newControl = unbindedControls.First();
                var newUIControl = newControl.GetControl();
                var newBindedControl = new BindedARControl(newUIControl, marker.Marker.Name);
                bindedControls.Add(newBindedControl);
                unbindedControls.Remove(newControl);

                ARControls.Children.Add(newUIControl);

                elementToDisplay = newBindedControl;

            }
            else if (result.Count() > 0)
            {
                // display ui element connected with marker
                elementToDisplay = result.First();
            }

            if (elementToDisplay != null)
            {
                elementToDisplay.UpdateLastSeen();
                ApplyTransformation(elementToDisplay, marker.Transformation);
            }

        }

        private void ApplyTransformation(BindedARControl element, Matrix3D baseTransformation)
        {
            
            // Create additional transformations
            var centerImageAtOrigin = Matrix3DFactory.CreateTranslation(-element.Control.ActualWidth * 0.5, -element.Control.ActualWidth * 0.5, 0);
            var invertYAxis = Matrix3DFactory.CreateScale(1, -1, 1);
            var viewport = Matrix3DFactory.CreateViewportTransformation(RootGrid.ActualWidth, RootGrid.ActualHeight);
            var rotate = Matrix3DFactory.CreateRotationZ(MathHelper.ToRadians(0));
            var scale = Matrix3DFactory.CreateScale(1);

            // Compose transform
            var m = Matrix3D.Identity;
            m = m * centerImageAtOrigin;
            m = m * invertYAxis;
            m = m * rotate;
            m = m * scale;
            m = m * baseTransformation;
            m = Matrix3DFactory.CreateViewportProjection(m, Matrix3D.Identity, projectionMatrix, viewport);

            // Apply transform
            element.Control.Projection = new Matrix3DProjection { ProjectionMatrix = m };
            element.Control.Visibility = System.Windows.Visibility.Visible;
        }






        // set video resolution
        private void videoFormatCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (captureSource != null)
            {
                captureSource.VideoCaptureDevice.DesiredFormat = (VideoFormat)e.AddedItems[0];

                if (captureSource.State == CaptureState.Started)
                {
                    captureSource.Stop();
                    captureSource.Start();
                }
            }
        }


        private void GlassBorder_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
        {
            if (!resolutionCombo.IsDropDownOpen)
            {
                HoverOut.Begin();
            }
        }

        private void GlassBorder_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
        {
            HoverIn.Begin();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {

            OpenFileDialog openFile = new OpenFileDialog();
            openFile.Multiselect = false;
            openFile.Filter = "XML File (*.xml)|*.xml";
            if (openFile.ShowDialog() == true)
            {
                XDocument inputXml = XDocument.Load(openFile.File.OpenRead());

                // while loading new file we must get rid of old binded and unbinded ArControls
                // as well as deleting unused objects that were displayed before and are in ARControls element

                unbindedControls = (from control in inputXml.Descendants("InputControls").Elements("Control")
                                    select new UnbindedARControl()
                                    {
                                        ID = control.Attribute("id").Value,
                                        Type = control.Attribute("type").Value,
                                        ExampleValue = control.Attribute("exampleValue").Value
                                    }).ToList<UnbindedARControl>();


                // clearing arcontrols.children and removing the binded collection is nessesary
                // because we do not want to leave any referenced objects

                ARControls.Children.Clear();
                bindedControls = new BindedARControlsList();

            }


        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            SaveFileDialog saveFile = new SaveFileDialog();
            if (saveFile.ShowDialog() == true)
            {
                var outputXml = saveFile.OpenFile();
                outputXml.Flush();
                outputXml.Close();
            }
        }

        // turn off projector mode
        private void projectorMode_Unchecked(object sender, RoutedEventArgs e)
        {
            WebcamVideo.Visibility = System.Windows.Visibility.Visible;

        }

        // turn on projector mode
        private void projectorMode_Checked(object sender, RoutedEventArgs e)
        {
            WebcamVideo.Visibility = System.Windows.Visibility.Collapsed;

        }

        private void start_Click(object sender, RoutedEventArgs e)
        {
            startButton_Click(sender, e);
        }
        
        // this one is to avoid stupid slide panel flicking back and forward
        private void resolutionCombo_DropDownClosed(object sender, EventArgs e)
        {
            HoverOut.Begin();
        }

        private void FullScreen_Checked(object sender, RoutedEventArgs e)
        {
            Application.Current.Host.Content.IsFullScreen = !Application.Current.Host.Content.IsFullScreen;
        }

        private void FullScreen_Unchecked(object sender, RoutedEventArgs e)
        {
            Application.Current.Host.Content.IsFullScreen = !Application.Current.Host.Content.IsFullScreen;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            Initialize();
        }

        private void Initialize()
        {
            // Init capture source
            captureSource = new CaptureSource();
            captureSource.VideoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
            resolutionCombo.ItemsSource = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice().SupportedFormats;
            
            // try to start with 640x480 resoluton
            captureSource.VideoCaptureDevice.DesiredFormat = new VideoFormat(PixelFormatType.Unknown, 640, 480, 30);

            arDetector = new CaptureSourceMarkerDetector(captureSource, 1, 4000, ApplicationMarkers());
            AttachMarkerDetectedEvent();

            // do the binding :)
            this.DataContext = this;
        }

    
    }
}
